#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>

#ifndef NON_OS
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#endif

#include <config.h>
#include <types.h>
#include <systems.h>
#include <gobjs.h>
#include <page.h>
#include <image.h>
#include <misc.h>
#include <unicode.h>
#include <gapi.h>
#include <font.h>
#include <linux_list.h>

int gid = 0;
extern int fb;
extern int xres, yres;
extern BOOLEAN defer_gobj_by_page;
extern PAGE *current_page;

int SetBackground(void *gobj, u8 *img_file, BOOLEAN auto_scale);
int SetForeground(void *gobj, u8 *img_file, u8 fg_align);
int SetBufferedImage(void *gobj, u8 *img_file, s8 index, BOOLEAN visible);
int SetText(void *gobj, unicode_t *string, u8 size, COLOR *color, 
            u8 *font_file, u32 font_size, u8 align, COORD *pos);
int SetVisible(void *gobj, u8 img_layer, BOOLEAN visible);
int SetActivate(void *gobj, BOOLEAN active);
int UpdateText(void *gobj, unicode_t *text);
int DrawGObj(void *gobj);
int RedrawGObj(void *gobj);

inline void __set_up_method(GOBJ *gobj)
{ 
    gobj->setBackground     = SetBackground;
    gobj->setForeground     = SetForeground;
    gobj->setBufferImage    = SetBufferedImage;
    gobj->setText           = SetText;
    gobj->setVisible        = SetVisible;
    gobj->setActivate       = SetActivate;
    gobj->updateBackground  = SetBackground;
    gobj->updateForeground  = SetForeground;
    gobj->updateBufferImage = SetBufferedImage;
    gobj->updateText        = UpdateText;
    gobj->draw              = DrawGObj;
    gobj->redraw            = RedrawGObj;
}

GOBJ* CreateGObj(u32 x, u32 y, u32 width, u32 height, COLOR *color)
{
    GOBJ *gobj;
    gobj = (GOBJ *)malloc(sizeof(GOBJ));
    if(gobj == NULL) {
        WARNING("Fault to allocate memory for Gobj\n");
        return NULL;
    }
    memset(gobj, 0x0, sizeof(GOBJ));
    __set_up_method(gobj);
    
    if(x > xres || y > yres)
        WARNING("GObj is out of screen (X: %d, Y: %d)\n", x, y);
        
    gobj->gid = gid++;    
    gobj->x = x;
    gobj->y = y;
    gobj->width = width;
    gobj->height = height;
    if(color) {
        gobj->color = (COLOR *)malloc(sizeof(COLOR));
        memcpy(gobj->color, color, sizeof(COLOR));
    }
    else
        gobj->color = NULL;
        
    gobj->activate = true;
    gobj->visible = true;
    //gobj->event_trigger_type = 0;
    INIT_LIST_HEAD(&gobj->pg_list);
    INIT_LIST_HEAD(&gobj->overlapped_gobj_list);
    //gobj->draw_hook = NULL;
    
    return gobj;
}

/*
 * Store background image data to memory, and initialize 
 * the background image object.
 */
int SetBackground(void *gobj, u8 *img_file, BOOLEAN auto_scale)
{
    GOBJ *obj = (GOBJ *)gobj;
    double ws = 0, hs = 0, scale = 0;
    int i, ret = 0;
    
    if(!obj) {
        return -1;
    }
    
    if(obj->background != NULL) {
        /* I have background image already, but I wanna update it with other image */
        if(obj->background->data)
            free(obj->background->data);

        obj->background->visible = false;
    }
    else {
        obj->background = (IMAGE *)malloc(sizeof(IMAGE));
        if(!obj->background) {
            ERROR("Can't allocate memory for object\n");
            return -2;
        }
        memset(obj->background, 0x0, sizeof(IMAGE));
        obj->background->x = obj->x;
        obj->background->y = obj->y;
        obj->background->tp = false;
        obj->background->visible = false;
      
        for(i = 0; i < GOBJ_IMG_LAYER; i++) {
            if(obj->img_layer_table[i] == NULL) {
                obj->img_layer_table[i] = obj->background;
                break;
            }
        }
    }
    
    if(img_file != NULL) {
         if((ret = load_image(obj->background, img_file)) < 0) {
             switch(ret) {
                 case -2: 
                     WARNING("%s: Not a supported image format!\n", img_file); 
                     break;
                 case -1: 
                 default: 
                     WARNING("%s: Cannot open image", img_file); 
                     break;
             }
             /* Image is not loaded, use default setting */
             obj->background->data = NULL;
             obj->background->width = obj->width;
             obj->background->height = obj->height;
             ret = -3;
        }
    }
    else {
        obj->background->data = NULL;
        obj->background->width = obj->width;
        obj->background->height = obj->height;
    }

     /* Do we have to scale image ? */
     if(auto_scale && obj->background->data != NULL) {
          if(obj->background->width - obj->width >= 0)
              ws = (double)obj->background->width/(double)obj->width;
          if(obj->background->height - obj->height >= 0)
            hs = (double)obj->background->height/(double)obj->height;
        scale = (ws > hs) ? ws : hs;
        GraphicScaleImage(obj->background, scale);
    }
    obj->background->visible = true;

    return ret;
}


/*
 * Store foreground image data to memory, and initialize 
 * the foreground image object.
 */
int SetForeground(void *gobj, u8 *img_file, u8 fg_align)
{
    GOBJ *obj = (GOBJ *)gobj;
    int i, ret = 0;
    
    if(!obj) {
        return -1;
    }
    
    if(obj->foreground) {
        /* I have foreground image already, but I wanna update it with other image */
        if(obj->foreground->data)
            free(obj->foreground->data);
        obj->foreground->visible = false;
    }
    else {
        obj->foreground = (IMAGE *)malloc(sizeof(IMAGE));
        if(!obj->foreground) {
            ERROR("Can't allocate memory for object\n");
            return -2;
        }
        memset(obj->foreground, 0x0, sizeof(IMAGE));
        obj->foreground->tp = false;
          obj->foreground->visible = false;
      
          for(i = 0; i < GOBJ_IMG_LAYER; i++) {
              if(obj->img_layer_table[i] == NULL) {
                  obj->img_layer_table[i] = obj->foreground;
                  break;
              }
          }
    }
    
    if(img_file != NULL) {
        if((ret = load_image(obj->foreground, img_file)) < 0) {
            switch(ret) {
                case -2: WARNING("%s: Not a supported image format!\n", img_file); break;
                case -1: 
                default: WARNING("%s: Cannot open image\n", img_file); break;
            }
            obj->foreground->data = NULL;
            obj->foreground->width = obj->width;
            obj->foreground->height = obj->height;
            ret = -3;
        }
    }
    else {
        obj->foreground->data = NULL;
        obj->foreground->width = obj->width;
        obj->foreground->height = obj->height;
    }
        
    obj->fg_align = fg_align;
    if(obj->fg_align & ALIGN_LEFT) {
        obj->foreground->x = obj->x;
    }
    else if(obj->fg_align & ALIGN_CENTER) {
        if(obj->foreground->width >= obj->width)
            obj->foreground->x = obj->x;
        else
            obj->foreground->x = obj->x + _round((obj->width - obj->foreground->width)/2);
    }
    else if(obj->fg_align & ALIGN_RIGHT) {
        if(obj->foreground->width >= obj->width)
            obj->foreground->x = obj->x;
        else
            obj->foreground->x = obj->x + obj->width - obj->foreground->width;
    }
    else { /* Default to LEFT */
        obj->foreground->x = obj->x;
    }
        
    if(obj->fg_align & ALIGN_TOP) {
        obj->foreground->y = obj->y;
    }
    else if(obj->fg_align & ALIGN_MIDDLE) {
        if(obj->foreground->height >= obj->height)
          obj->foreground->y = obj->y;
        else
          obj->foreground->y = obj->y + _round((obj->height - obj->foreground->height)/2);
    }
    else if(obj->fg_align & ALIGN_BOTTOM) {
        if(obj->foreground->height >= obj->height)
            obj->foreground->y = obj->y;
        else
            obj->foreground->y = obj->y + obj->height - obj->foreground->height;
    }
    else { /* Default to TOP */
        obj->foreground->y = obj->y;
    }
    obj->foreground->visible = true;
  
    return ret;
}


/*
 * Store buffered image data to memory, and initialize 
 * the buffered image object.
 */
int SetBufferedImage(void *gobj, u8 *img_file, s8 index, BOOLEAN visible)
{
    GOBJ *obj = (GOBJ *)gobj;
    int i, ret = 0;
    
    if(index < 0 || index >= BUFFERED_IMAGE_AMOUNT)
        return -4;
    
    if(!obj)
        return -1;
    
    if(obj->buffered_img[(u8)index]) {
        /* I have buffered image already, but I wanna update it with other image */
        if(obj->buffered_img[(u8)index]->data)
            free(obj->buffered_img[(u8)index]->data);
    }
    else {
        obj->buffered_img[(u8)index] = (IMAGE *)malloc(sizeof(IMAGE));
        if(!obj->buffered_img[(u8)index]) {
            ERROR("Can't allocate memory for object\n");
            return -2;
        }
        memset(obj->buffered_img[(u8)index], 0x0, sizeof(IMAGE));
        obj->buffered_img[(u8)index]->x = obj->x;
        obj->buffered_img[(u8)index]->y = obj->y;
        obj->buffered_img[(u8)index]->tp = false;
        obj->buffered_img[(u8)index]->visible = visible;
    
        for(i = 0; i < GOBJ_IMG_LAYER; i++) {
            if(obj->img_layer_table[i] == NULL) {
                obj->img_layer_table[i] = obj->buffered_img[(u8)index];
                break;
            }
        }
    }
    
    if(img_file != NULL) {
        if((ret = load_image(obj->buffered_img[(u8)index], img_file)) < 0) {
            switch(ret) {
                case -2: 
                    WARNING("%s: Not a supported image format!\n", img_file); 
                    break;
                case -1:
                default: 
                    WARNING("%s: Cannot open image\n", img_file); 
                    break;
            }
            free(obj->buffered_img[(u8)index]);
            obj->buffered_img[(u8)index] = NULL;
            return -3;
        }
    }
    else {
        obj->buffered_img[(u8)index]->data = NULL;
        obj->buffered_img[(u8)index]->width = obj->width;
        obj->buffered_img[(u8)index]->height = obj->height;
        obj->buffered_img[(u8)index]->visible = false;
    }
  
    return 0;
}


int SetText(void *gobj, unicode_t *string, u8 size, COLOR *color, u8 *font_file, u32 font_size, u8 align, COORD *pos)
{
#ifdef FONT_SUPPORT
    int i, ret = 0;
    BOOLEAN update = false;
    GOBJ *obj = (GOBJ *)gobj;
    
    if(!obj)
        return -1;
    
    if(obj->text) {
        /* We should call the UpdateText() */
        update = true;
    }
    else {
        /* First time setting text */
        obj->text = (TEXT *)malloc(sizeof(TEXT));
        if(!obj->text) {
            ERROR("Can't allocate memory for object\n");
            return -2;
        }
        memset(obj->text, 0x0, sizeof(TEXT));
    }
    
    if(size < FONT_SIZE_MINIMUM)
        obj->text->size = FONT_SIZE_MINIMUM;
    else
        obj->text->size = size;
    obj->text->fg_color = color;
#ifdef NON_OS
    obj->text->bg_color = obj->color;
#else
    obj->text->bg_color = pCOLOR_TRANSPARENT;
#endif
    obj->text->visible = false;
    obj->text->tp = true;
    obj->text->font_file = font_file;
    obj->text->font_size = font_size;
    
    if(update)
        return UpdateText(gobj, string);
    
    if(!string) {
        obj->text->txt_count = 0;
        obj->text->data = NULL;
        obj->text->char_width = NULL;
        obj->text->char_height = NULL;
        obj->text->string = NULL;
        obj->text->txt_width = 0;
        obj->text->txt_height = 0;
        goto no_text;
    }
        
    obj->text->string = uni_strdup(string);
    if(!obj->text->string) {
        ERROR("Unicode string duplication failed!\n");
        return -3;
    }
    
    if(font_file) {
        if((ret = load_font(obj->text, font_file)) < 0) {
            switch(ret) {
                case -1:
                    WARNING("Text object or font file is not set properly\n");
                    break;
                case -2:
                    WARNING("Fault to allocate text buffer\n");
                    break;
                case -3:
                    WARNING("Fault to read font file\n");
                    break;
                case -4:
                default:
                    WARNING("Font format is not supported\n");
                    break;
            }
            free(obj->text);
            obj->text = NULL;
            return -4;
        }
    }
    else {
        WARNING("Font file is not set\n");
        free(obj->text);
        obj->text = NULL;
        return -5;
    }
    
no_text:
    /* Text alignment setting */
    obj->text_align = align;
    if(obj->text_align & ALIGN_LEFT) {
        obj->text->x = obj->x;
    }
    else if(obj->text_align & ALIGN_CENTER) {
        if(obj->text->txt_width >= obj->width)
            obj->text->x = obj->x;
        else
          obj->text->x = obj->x + _round((obj->width - obj->text->txt_width)/2);
    }
    else if(obj->text_align & ALIGN_RIGHT) {
        if(obj->text->txt_width >= obj->width)
            obj->text->x = obj->x;
        else
          obj->text->x = obj->x + obj->width - obj->text->txt_width;
    }
    else { /* Coordination is used */
        if(pos && pos->x >= 0)
            obj->text->x = obj->x + pos->x;
        else
            obj->text->x = obj->x;  /* Default to LEFT */
    }
        
    if(obj->text_align & ALIGN_TOP) {
        obj->text->y = obj->y;
    }
    else if(obj->text_align & ALIGN_MIDDLE) {
        if(obj->text->txt_height >= obj->height)
            obj->text->y = obj->y;
        else
            obj->text->y = obj->y + _round((obj->height - obj->text->txt_height)/2);
    }
    else if(obj->text_align & ALIGN_BOTTOM) {
        if(obj->text->txt_height >= obj->height)
            obj->text->y = obj->y;
        else
            obj->text->y = obj->y + obj->height - obj->text->txt_height;
    }
    else { /* Coordination is used */
        if(pos && pos->y >= 0)
            obj->text->y = obj->y + pos->y;
        else
            obj->text->y = obj->y;  /* Default to TOP */
    }
    
    for(i = 0; i < GOBJ_IMG_LAYER; i++) {
        if(obj->img_layer_table[i] == NULL) {
            obj->img_layer_table[i] = (IMAGE *)TEXT_MAGIC;
            break;
        }
    }
    obj->text->visible = true;
#endif /* FONT_SUPPORT */
    return 0;
}

int SetVisible(void *gobj, u8 img_layer, BOOLEAN visible)
{
    GOBJ *obj = (GOBJ *)gobj;
    
    if(!gobj) return -1;
        
    switch(img_layer) {
    case IMG_LAYER_ALL:
        obj->visible = visible;
        
        if(obj->background)
            obj->background->visible = visible;
        if(obj->foreground)
            obj->foreground->visible = visible;
        if(obj->text)
            obj->text->visible = visible;
        if(obj->buffered_img[0])
            obj->buffered_img[0]->visible = visible;
        if(obj->buffered_img[1])
            obj->buffered_img[1]->visible = visible;
        if(obj->buffered_img[2])
            obj->buffered_img[2]->visible = visible;
        break;
    case IMG_LAYER_BACKGROUND:
        if(obj->background)
            obj->background->visible = visible;
        break;
    case IMG_LAYER_FOREGROUND:
        if(obj->foreground)
            obj->foreground->visible = visible;
        break;
    case IMG_LAYER_TEXT:
        if(obj->text)
            obj->text->visible = visible;
        break;
    case IMG_LAYER_BUFFERIMG0:
        if(obj->buffered_img[0])
            obj->buffered_img[0]->visible = visible;
        break;
    case IMG_LAYER_BUFFERIMG1:
        if(obj->buffered_img[1])
            obj->buffered_img[1]->visible = visible;
        break;
    case IMG_LAYER_BUFFERIMG2:
        if(obj->buffered_img[2])
            obj->buffered_img[2]->visible = visible;
        break;
    }
    return 0;
}

int SetActivate(void *gobj, BOOLEAN active)
{
    GOBJ *obj = (GOBJ *)gobj;
    obj->activate = active;
    return 0;
}

int UpdateText(void *gobj, unicode_t *string)
{
#ifdef FONT_SUPPORT
    int ret, i;
    int new_str_count = uni_strlen(string);
    GOBJ *obj = (GOBJ *)gobj;
    
    if(!obj || !obj->text)
        return -1;
    
    if(!string || new_str_count == 0) {
        /* UpdateText with empty string, clear text object */
        //DPRINTF("Freeing text data of object\n");
        return free_font(obj->text);
    }
    
    if(obj->text->txt_count == new_str_count && 
       obj->text->txt_count > 0 && 
       obj->text->bg_color == pCOLOR_TRANSPARENT) {
        if(!memcmp(obj->text->string, string, sizeof(unicode_t) * obj->text->txt_count))
            return 1; /* No changes for the string */
    }
            
    obj->text->visible = false;
    
    if(obj->text->font_file) {
        if((ret = reload_font(obj->text, string)) < 0) {
            switch(ret) {
                case -1:
                    WARNING("Text object or font file is not set properly\n");
                    break;
                case -2:
                    WARNING("Fault to allocate text buffer\n");
                    break;
                case -3:
                    WARNING("Fault to read font file\n");
                    break;
                case -4:
                default:
                    WARNING("Font format is not supported\n");
                    break;
            }
            return -3;
        }
    }
    else {
        WARNING("Font file is not set\n");
        return -4;
    }
    
    if(obj->text->string && string != obj->text->string)
        free(obj->text->string);
    
    if(string != obj->text->string) {    
        obj->text->string = uni_strdup(string);
        if(!obj->text->string) {
            ERROR("Unicode string duplication failed!\n");
            return -2;
        }
    }
    
    /* Text alignment setting */
    if(obj->text_align & ALIGN_LEFT) {
        obj->text->x = obj->x;
    }
    else if(obj->text_align & ALIGN_CENTER) {
        if(obj->text->txt_width >= obj->width)
            obj->text->x = obj->x;
        else
            obj->text->x = obj->x + _round((obj->width - obj->text->txt_width)/2);
    }
    else if(obj->text_align & ALIGN_RIGHT) {
        if(obj->text->txt_width >= obj->width)
            obj->text->x = obj->x;
        else
            obj->text->x = obj->x + obj->width - obj->text->txt_width;
    }
        
    if(obj->text_align & ALIGN_TOP) {
        obj->text->y = obj->y;
    }
    else if(obj->text_align & ALIGN_MIDDLE) {
        if(obj->text->txt_height >= obj->height)
            obj->text->y = obj->y;
        else
            obj->text->y = obj->y + _round((obj->height - obj->text->txt_height)/2);
    }
    else if(obj->text_align & ALIGN_BOTTOM) {
        if(obj->text->txt_height >= obj->height)
            obj->text->y = obj->y;
        else
            obj->text->y = obj->y + obj->height - obj->text->txt_height;
    }
    
    for(i = 0; i < GOBJ_IMG_LAYER; i++) {
        if(obj->img_layer_table[i] == NULL) {
            obj->img_layer_table[i] = (IMAGE *)TEXT_MAGIC;
            break;
        }
        else if((u32)obj->img_layer_table[i] == TEXT_MAGIC)
            break;
    }
    obj->text->visible = true;
  
#endif /* FONT_SUPPORT */
    return 0;
}

/*
 * Drawing out the Graphic object.
 */
int DrawGObj(void *gobj)
{
    int i;
#ifdef GPU_SUPPORT
    u8 cmd = 0;
#endif
    GOBJ *obj = (GOBJ *)gobj;
    RECT fillrect;
    
    if(!obj)
        return -1;
#if 0
    /* 
     *   i think we don't need the following page checking routine bacause i assume 
     *   users knows what they are doing while calling DrawGObj method.
     */
    BOOLEAN ugotme = false;
    /* check out if this object certainly belongs to some pages */
    if(!list_empty(&obj->pg_list)) {
        PAGE_LIST *pos, *tmp;
        list_for_each_entry_safe(pos, tmp, &obj->pg_list, list) {
            //DPRINTF("pos->page: %x, current_page: %x\n", pos->page, current_page);
            /* Check out this object belongs to current_page ? */
            if(pos->page == current_page) {
                ugotme = true;
                break;
            }
        }
    }
    else ugotme = true;   /* this object doesn't belong to any page, just draw it */
    
    if(!ugotme)
        return 0;
#endif
    
    if(!obj->visible)
        return 0;
        
    GraphicDeviceLock();
    
    if(obj->color) {
        fillrect.x = obj->x;
        fillrect.y = obj->y;
        fillrect.width = obj->width;
        fillrect.height = obj->height;
        fillrect.color = obj->color;
#ifdef GPU_SUPPORT 
        fillrect.hw_dep_prop.command = 0;
        fillrect.hw_dep_prop.end_layer = false;
        fillrect.hw_dep_prop.scale = 0;
        fillrect.hw_dep_prop.angle = 0;
#endif
        fillrect.filled = true;
        fillrect.tp = false;
        DrawRect(&fillrect);
    }
    
    for(i = 0; i < GOBJ_IMG_LAYER; i++) {
        if((u32)obj->img_layer_table[i] == TEXT_MAGIC) {
            if(obj->text->visible) {
                obj->text->multilines = obj->text_multilines;
                if(obj->text_multilines)
                    obj->text->line_count = (u32)ceil((double)obj->text->txt_width/(double)obj->width);
                else
                    obj->text->line_count = 1;
                obj->text->line_width = obj->width;
                DrawText(obj->text);
#ifdef GPU_SUPPORT
                cmd = obj->text->hw_dep_prop.command;
#endif
            }
        }
        else if(obj->img_layer_table[i] && 
                obj->img_layer_table[i]->data && 
                obj->img_layer_table[i]->visible) {
            DrawImage(obj->img_layer_table[i]);
#ifdef GPU_SUPPORT            
            cmd = obj->img_layer_table[i]->hw_dep_prop.command;
#endif
        }
    }
    
    if(obj->draw_hook)
        obj->draw_hook(obj);
    
    if(!defer_gobj_by_page)
        GraphicFlush();

    GraphicDeviceUnlock();
    
    return 0;
}

#define TAG_DRAWED 0x5A
char *gobj_tag;

void __draw_out_overlapped_gobj(GOBJ *gobj)
{
    OVERLAPPED_GOBJ_LINK_NODE *overlapped_node;
    struct list_head *node;
    
    /* 
     * Draw out the overlapped GObj assciated to gobj by traversing 
     * the new link we just created.
     */
    if(!list_empty(&gobj->overlapped_gobj_list)) {
        list_for_each(node, &gobj->overlapped_gobj_list) {
            overlapped_node = container_of(node, OVERLAPPED_GOBJ_LINK_NODE, list);
            if(gobj_tag[overlapped_node->gobj->pid] == TAG_DRAWED)
                continue;

            overlapped_node->gobj->draw((void *)overlapped_node->gobj);
            /* Tag this gobj as HAS_BEEN_DRAWED */
            gobj_tag[overlapped_node->gobj->pid] = TAG_DRAWED;
        }

        /* Draw out all overlapped GObj recursively */
        list_for_each(node, &gobj->overlapped_gobj_list) {
            overlapped_node = container_of(node, OVERLAPPED_GOBJ_LINK_NODE, list);
            __draw_out_overlapped_gobj(overlapped_node->gobj);
        }
    }
}

/*
 * Redrawing out the Graphic object.
 */
int RedrawGObj(void *gobj)
{
    GOBJ *obj = (GOBJ *)gobj;
    
    if(!obj) 
        return -1;
    
    /* If GObj is set to invisible, then redraw the whole page to make it invisible */
    if(!obj->visible) {
        PGDisplay(current_page);
        return 0;
    }
    
    /* Allocate gobj_tag array to keep track of redrawed gobj */
    gobj_tag = (char *)calloc(1, current_page->gobj_counter + 1);
    
    defer_gobj_by_page = true;
    
    /* Draw out this GObj */
    obj->draw(obj);
    gobj_tag[obj->pid] = TAG_DRAWED;
    
    /* Recursively draw out the overlapped GObjs */
    __draw_out_overlapped_gobj(obj);

    GraphicFlush();

    defer_gobj_by_page = false;
    
    free(gobj_tag);
    
    return 0;
}


/*
 * Destroy Graphic object by freeing all contents and 
 * data structures in it.
 */
int DestroyGObj(GOBJ *obj)
{
    int i;
    
    if(!obj)
        return -1;
    
    if(obj->foreground) {
        free(obj->foreground->data);
        free(obj->foreground);
    }
    
    if(obj->background) {
        free(obj->background->data);
        free(obj->background);
    }

    for(i = 0; i < BUFFERED_IMG_ALL; i++) {
        if(obj->buffered_img[i]) {
            free(obj->buffered_img[i]->data);
            free(obj->buffered_img[i]);
        }
    }
    
#ifdef FONT_SUPPORT
    if(obj->text) {
        free_font(obj->text);
        free(obj->text);
    }
#endif
    
    if(obj->color) {
        free(obj->color);
    }
    
    free(obj);
    obj = NULL;
    
    return 0;
}


